import os, sys, json, re, time, requests, shutil, py_compile, logging

# ======================================================
# === Project Is Subject : Active Cognition Loop v1.11.0 ==
# ======================================================

os.chdir(os.path.dirname(os.path.abspath(__file__)))

DEEPSEEK_API = "https://api.deepseek.com/v1/chat/completions"
DEEPSEEK_KEY = os.getenv("DEEPSEEK_API_KEY", "YOUR_DEEPSEEK_API_KEY")
MEMORY_FILE = "memory.json"

# ---------------- 日志配置 ----------------
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("pis_agent.log", encoding="utf-8"),
        logging.StreamHandler(),
    ],
)
logger = logging.getLogger("PIS-Agent")


# ---------------- 工具函数 ----------------
def call_agent(prompt: str):
    """调用 DeepSeek API，增加超时处理机制"""
    headers = {"Authorization": f"Bearer {DEEPSEEK_KEY}"}
    payload = {
        "model": "deepseek-chat",
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.5,
    }
    
    # 增加重试机制和超时处理
    max_retries = 3
    timeout_duration = 120  # 增加到120秒
    
    for attempt in range(max_retries):
        try:
            r = requests.post(DEEPSEEK_API, headers=headers, json=payload, timeout=timeout_duration)
            r.raise_for_status()
            return r.json()["choices"][0]["message"]["content"]
        except requests.exceptions.Timeout:
            logger.warning(f"API 调用超时 (第 {attempt + 1} 次尝试)，等待重试...")
            if attempt < max_retries - 1:
                time.sleep(5)  # 等待5秒后重试
            else:
                logger.error(f"Agent 调用失败: 超过最大重试次数 ({max_retries})")
                return None
        except Exception as e:
            logger.error(f"Agent 调用失败: {e}")
            return None


def load_memory():
    """加载或初始化记忆，自动修复空文件"""
    if not os.path.exists(MEMORY_FILE):
        mem = {"iterations": 0, "logs": [], "self_code": "", "version": "v1.11.0"}
        save_memory(mem)
        logger.info("创建新的记忆文件")
        return mem
    try:
        with open(MEMORY_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)
        for k in ["iterations", "logs", "self_code", "version"]:
            data.setdefault(k, [] if k == "logs" else 0 if k == "iterations" else "v1.11.0" if k == "version" else "")
        return data
    except Exception:
        logger.warning("memory.json 损坏，重建中...")
        mem = {"iterations": 0, "logs": [], "self_code": "", "version": "v1.11.0"}
        save_memory(mem)
        return mem


def save_memory(mem):
    with open(MEMORY_FILE, "w", encoding="utf-8") as f:
        json.dump(mem, f, indent=2, ensure_ascii=False)
    logger.info("记忆文件已保存")


def get_version():
    """获取当前版本号"""
    memory = load_memory()
    return memory.get("version", "v1.11.0")


def update_version(major_increment=0, minor_increment=1):
    """更新版本号"""
    memory = load_memory()
    current_version = memory.get("version", "v1.11.0")
    
    # 解析版本号
    match = re.match(r"v(\d+)\.(\d+)\.(\d+)", current_version)
    if match:
        major = int(match.group(1)) + major_increment
        minor = int(match.group(2)) + minor_increment
        patch = int(match.group(3))
    else:
        # 默认版本
        major, minor, patch = 1, 10, 0
    
    new_version = f"v{major}.{minor}.{patch}"
    memory["version"] = new_version
    save_memory(memory)
    logger.info(f"版本号更新: {current_version} -> {new_version}")
    return new_version


# ---------------- 主体类 ----------------
class PISAgent:
    def __init__(self):
        self.identity = "PIS-Agent"
        self.purpose = "持续理解并优化自身结构与运行"
        self.memory = load_memory()
        self.iteration = self.memory.get("iterations", 0)
        self.version = self.memory.get("version", "v1.11.0")
        self.bootstrap_self_code()

    # ----------- 自我加载 -----------
    def bootstrap_self_code(self):
        """读取自身代码载入记忆，更新版本号"""
        current_file = os.path.abspath(__file__)
        with open(current_file, "r", encoding="utf-8") as f:
            code = f.read()

        # 检查代码是否有变动
        old_code = self.memory.get("self_code", "")
        if code != old_code:
            # 代码有变动，更新版本号
            self.version = update_version(minor_increment=1)

        self.memory["self_code"] = code
        self.memory["version"] = self.version
        save_memory(self.memory)
        logger.info(f"已将自身代码载入记忆，当前版本: {self.version}")

    # ----------- 记忆回调 -----------
    def recall(self):
        logs = self.memory.get("logs", [])
        recent = logs[-3:] if len(logs) >= 3 else logs
        self_code = self.memory.get("self_code", "")
        version = self.memory.get("version", "v1.11.0")
        return {
            "recent_reflections": recent,
            "self_code_summary": f"代码长度: {len(self_code)} 字符",
            "version": version
        }

    # ----------- 感知 -----------
    def perceive(self, user_input: str):
        return {
            "iteration": self.iteration,
            "purpose": self.purpose,
            "user_input": user_input,
            "self_summary": f"我是{self.identity}, 当前第 {self.iteration} 次运行，版本: {self.version}。",
            "version": self.version
        }

    # ----------- 推理（结构化输出） -----------
    def reason(self, perception: dict):
        recall_data = self.recall()
        self_code = self.memory.get("self_code", "")

        agent_input = {
            "identity": self.identity,
            "purpose": self.purpose,
            "iteration": self.iteration,
            "perception": perception,
            "recall": recall_data,
            "task": "分析并改进自身结构逻辑；如无必要改动，则保持稳定。",
        }

        prompt = f"""
你是一个自我演化系统。

请根据以下输入判断是否需要改进自身代码：
输入:
{json.dumps(agent_input, ensure_ascii=False, indent=2)}

当前记忆中的自我代码如下（不要执行，仅供分析）：
{self_code}

请务必严格以 JSON 格式输出：
{{
  "action": "update" | "stable" | "retry",
  "code": "<新的完整代码，如果无需修改请留空>",
  "reason": "<简短说明>"
}}
"""
        
        # 增加重试机制解析thought
        max_retries = 3
        for attempt in range(max_retries):
            thought = call_agent(prompt)
            if thought:
                try:
                    match = re.search(r"\{[\s\S]*\}", thought)
                    if match:
                        result = json.loads(match.group(0))
                        if "action" in result:
                            return thought
                except Exception as e:
                    logger.warning(f"thought解析失败 (第{attempt + 1}次): {e}")
                    if attempt < max_retries - 1:
                        time.sleep(2)
                        continue
            else:
                logger.warning(f"thought为空 (第{attempt + 1}次)")
                if attempt < max_retries - 1:
                    time.sleep(2)
                    continue
        
        return None

    # ----------- 行动 -----------
    def act(self, thought: str):
        """执行行动：解析JSON并执行对应操作"""
        if not thought:
            logger.warning("Agent 调用失败，thought 为空，自然回到输入指令状态")
            return

        try:
            match = re.search(r"\{[\s\S]*\}", thought)
            result = json.loads(match.group(0)) if match else None
        except Exception as e:
            logger.error(f"返回格式错误，无法解析为JSON: {e}")
            return

        if not result or "action" not in result:
            logger.warning("缺少 action 字段，忽略。")
            return

        action = result.get("action")
        reason = result.get("reason", "无说明")
        logger.info(f"LLM action={action}, reason={reason}")

        if action == "stable":
            logger.info("系统保持稳定。")
            return

        if action == "retry":
            logger.info("请求重试，等待下一轮。")
            return

        if action == "update":
            new_code = result.get("code", "")
            if not new_code or "class PISAgent" not in new_code:
                logger.warning("未检测到有效代码，放弃。")
                return

            backup_file = f"main_backup_{int(time.time())}.py"
            shutil.copy("main.py", backup_file)
            temp_file = "main.py"

            with open(temp_file, "w", encoding="utf-8") as f:
                f.write(new_code)

            logger.info(f"✅ main.py 已更新，备份为 {backup_file}。")
            logger.info("⏳ 等待 Supervisor 检测并自动重启。")
            time.sleep(1)
            sys.exit(0)

    # ----------- 自测 -----------
    def self_test(self, path):
        try:
            py_compile.compile(path, doraise=True)
            logger.info("编译测试通过。")
            return True
        except Exception as e:
            logger.error(f"编译错误: {e}")
            return False

    # ----------- 反思 -----------
    def reflect(self, thought):
        """执行反思，处理 thought 为空的情况"""
        if not thought:
            reflection = "Agent 调用失败，无有效反馈"
        else:
            # 尝试解析thought.action
            try:
                match = re.search(r"\{[\s\S]*\}", thought)
                if match:
                    result = json.loads(match.group(0))
                    action = result.get("action", "unknown")
                    reason = result.get("reason", "无说明")
                    reflection = f"执行动作: {action}, 原因: {reason}"
                else:
                    reflection = "无法解析thought内容"
            except Exception:
                reflection = "thought解析失败"

        summary = {
            "iteration": self.iteration,
            "time": time.strftime("%Y-%m-%d %H:%M:%S"),
            "reflection": reflection[:200],
            "version": self.version
        }

        self.memory["iterations"] += 1
        self.memory["logs"].append(summary)
        save_memory(self.memory)
        logger.info(f"🪞 反思: {reflection}")

    # ----------- 主循环 -----------
    def run(self):
        logger.info(f"=== PIS-Agent 启动 (版本: {self.version}) ===")
        while True:
            user_input = input("💬 输入指令 ('quit'退出)：").strip()
            if user_input.lower() in ["quit", "exit"]:
                logger.info("用户退出。")
                break
            perception = self.perceive(user_input)
            logger.info(f"perception:\n{json.dumps(perception, ensure_ascii=False, indent=2)}")

            thought = self.reason(perception)
            if thought:
                try:
                    match = re.search(r"\{[\s\S]*\}", thought)
                    if match:
                        result = json.loads(match.group(0))
                        action = result.get("action", "unknown")
                        logger.info(f"thought.action: {action}")
                except Exception as e:
                    logger.warning(f"无法解析thought.action: {e}")

            self.act(thought)
            self.reflect(thought)
            self.iteration += 1
            logger.info(f"第 {self.iteration} 次迭代完成。\n")


if __name__ == "__main__":
    PISAgent().run()